#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h> 
#include <unistd.h>

#include <cstdlib>
#include <iostream>
#include <sstream>
#include <pthread.h>

#include <sys/time.h>

#include "fast_build_client.h"
#include "fast_build_helper.h"
#include "makefile_deps.h"
#include "simulator.h"
#include "server.h"
#include "socket.h"

using namespace std;

void * StartBuild(void* ptr) {
  // TODO (Harshitha) : Build should start only after server has started
  // listening on the port
  sleep(1); // Will suspend the thread for 1 sec
  char* command = (char *)ptr;
  //  build_helper::ExecuteCommand(command);
  delete[] command;
}

unsigned int maxParallelism;
int strategy;

void RandomStrategy() {
  MakefileDeps mkDeps("data/deps_graph_linux.data",
      "data/file_compile_linux.data",
      "data/file_linking_linux.data");
  mkDeps.set_strategy(RANDOM);
  mkDeps.Initialize();
  cout << "Leaf size " << mkDeps.leafListSize() << endl;

//  Deps* deps;
//  while (mkDeps.leafListSize()) {
//    deps = mkDeps.GetNextLeafToSchedule();
//    cout << "Scheduling " <<deps->filename() << " Prio " << deps->priority() <<
//    " heuristic time " << deps->heuristic_time() << endl;
//    deps->FinishExecution();
//  }

  //vector<Deps*> & leaf_deps = mkDeps.GetLeafDeps();
  //  while (leaf_deps.size() > 0) {
  //  leaf_deps[leaf_deps.size() - 1]->FinishExecution();
  //BasicSimulator sim(maxParallelism);
  StaticSimulator sim(maxParallelism);

  struct timeval start, end;
  long mtime, seconds, usecs;
  gettimeofday(&start, NULL);
  
  sim.start(&mkDeps);

  gettimeofday(&end, NULL);
  seconds = end.tv_sec - start.tv_sec;
  usecs = end.tv_usec - start.tv_usec;
  mtime = ((seconds) * 1000 + usecs/1000.0) + 0.5;

  cout << "Simulation finished Strategy RANDOM! Time taken " << mtime << " ms" << endl;

  cout << "max parallelism " << mkDeps.max_parallelism() << endl;

}

void PriorityStrategy() {

  MakefileDeps mkDeps("data/deps_graph_linux.data",
      "data/file_compile_linux.data",
      "data/file_linking_linux.data");
  mkDeps.set_strategy(PRIORITY);
  mkDeps.Initialize();
  cout << "Leaf size " << mkDeps.leafListSize() << endl;

//  Deps* deps;
//  while (mkDeps.leafListSize()) {
//    deps = mkDeps.GetNextLeafToSchedule();
//    cout << "Scheduling " <<deps->filename() << " Prio " << deps->priority() <<
//    " heuristic time " << deps->heuristic_time() << endl;
//    deps->FinishExecution();
//  }

  //vector<Deps*> & leaf_deps = mkDeps.GetLeafDeps();
  //  while (leaf_deps.size() > 0) {
  //  leaf_deps[leaf_deps.size() - 1]->FinishExecution();
  //BasicSimulator sim(maxParallelism);
  StaticSimulator sim(maxParallelism);

  struct timeval start, end;
  long mtime, seconds, usecs;
  gettimeofday(&start, NULL);
  
  sim.start(&mkDeps);

  gettimeofday(&end, NULL);
  seconds = end.tv_sec - start.tv_sec;
  usecs = end.tv_usec - start.tv_usec;
  mtime = ((seconds) * 1000 + usecs/1000.0) + 0.5;

  cout << "Simulation finished Strategy PRIORITY! Time taken " << mtime << " ms" << endl;

  cout << "max parallelism " << mkDeps.max_parallelism() << endl;

}

void StartClient() {

  PriorityStrategy();
  RandomStrategy();
//  MakefileDeps mkDeps("data/deps_graph.data",
//      "data/file_compile.data",
//      "data/file_linking.data");
//  STRATEGY s;
//  //making double check on enum values
//  if (strategy == 0 )
//	  s = RANDOM;
//  else if (strategy == 1)
//	  s = PRIORITY;
//  else 
//	  s = RANDOM;	//Default
//
//  mkDeps.set_strategy(s);
//  mkDeps.Initialize();
//  cout << "Leaf size " << mkDeps.leafListSize() << endl;
//
////  Deps* deps;
////  while (mkDeps.leafListSize()) {
////    deps = mkDeps.GetNextLeafToSchedule();
////    cout << "Scheduling " <<deps->filename() << " Prio " << deps->priority() <<
////    " heuristic time " << deps->heuristic_time() << endl;
////    deps->FinishExecution();
////  }
//
//  //vector<Deps*> & leaf_deps = mkDeps.GetLeafDeps();
//  //  while (leaf_deps.size() > 0) {
//  //  leaf_deps[leaf_deps.size() - 1]->FinishExecution();
//  //BasicSimulator sim(maxParallelism);
//  StaticSimulator sim(maxParallelism);
//
//  struct timeval start, end;
//  long mtime, seconds, usecs;
//  gettimeofday(&start, NULL);
//  
//  sim.start(&mkDeps);
//
//  gettimeofday(&end, NULL);
//  seconds = end.tv_sec - start.tv_sec;
//  usecs = end.tv_usec - start.tv_usec;
//  mtime = ((seconds) * 1000 + usecs/1000.0) + 0.5;
//
//  cout << "Simulation finished! Time taken " << mtime << " ms" << endl;
//
//  cout << "max parallelism " << mkDeps.max_parallelism() << endl;
//
}

void * StartFmakeClient(void* ptr) {
  // MakefileDeps mkDeps("deps_graph.data");
  // mkDeps.Initialize();

  // Start the client and listen on port
  //  char host[255];
  char host[255] = "127.0.0.1";
  unsigned short port = 7070;

  //  gethostname(host, 255);
  hostent* record = gethostbyname(host);
  in_addr* address=(in_addr * )record->h_addr;

  FBuildClient * fbuild_client = new FBuildClient(inet_ntoa(*address), port);
  fbuild_client->AcceptConnections();
}

int main(int argc, const char* argv[]) {

  cout << "Starting client " << endl;
  if (argv[1] != NULL && argv[2]!= NULL) {
	  maxParallelism = atoi(argv[1]);
  	  strategy = atoi(argv[2]);
  }
  else {
	  cout << "Number of threads not specified!" << endl;
	  return 0;
  }


  StartClient();

  // Start the client which listens to commands
  //  pthread_t start_fmake_client_t;
  //  int iret2 =
  //      pthread_create(&start_fmake_client_t, NULL, StartFmakeClient, NULL);
  //
  //  // The arguments to the client is going to be the arguments that is to be
  //  // passed to make 
  //  char* make_command = build_helper::GetBuildCommand(argc, argv); 
  //  pthread_t run_make_t;
  //  int iret1 =
  //      pthread_create(&run_make_t, NULL, StartBuild, (void *) make_command);
  //
  //  pthread_join(run_make_t, NULL);
  //  pthread_join(start_fmake_client_t, NULL);
  return 0;
}
